#!/usr/bin/ruby
# Author: Daniel "Trizen" Șuteu
# License: GPLv3
# Date: 21 April 2014
# Website: http://github.com/trizen
# Function to parse the input file into a 2D-matrix
func parse_table(file) {
file.open_r(\var fh) || return();
var table = [fh.readline.trim_end.split("\t")];
while (fh.readline(\var line)) {
line ~~ /\S/ || next;
table.append(line.trim_end.split("\t"));
}
return table;
}
# Function to write a tab-delimited file from a 2D-matrix
func output_table(table, file) {
file.open_w(\var fh) || return();
table.each { |row|
fh.say(row.join("\t"));
};
fh.close;
}
func usage {
"usage: #{__FILE__} [input.txt] [output.txt]\n".die;
}
#
## The main work starts here
#
var input_file = File.new(ARGV[0] \\ usage());
var output_file = File.new(ARGV[1] \\ usage());
input_file.exists || (
"input file `#{input_file}' does not exists!\n".die;
);
# Parse the input file into a 2D-matrix
var table = parse_table(input_file);
var cols = table.shift; # column names
var lim = table.pop; # limit values
var ngifts = (cols.end - 1); # the number of gifts
# Map the limit of gifts
var limit = Hash.new;
for i in (1..ngifts) {
limit{cols[i]} = Num(lim[i])
}
var people = table.range.map { |i|
[
# [0] == people name
table[i][0],
# [1] == gift preferences
range(1, ngifts).map { |j|
[
cols[j], # [1][0] == gift name
Num(table[i][j]), # [1][1] == gift rank
]
},
# [2] == weight
Num(table[i][-1]),
]
};
# Put each preference into a distinct group
var rank = [];
for i in (1..ngifts) {
rank.append(
# By shuffling the results, we treat everyone equally
# (here we can sort by weight, if we provide different weights)
people.map { |row|
[row[0], row[1].find {|item| item[1] == i}, row[2]]
}.shuffle;
);
}
var served = Hash.new;
var matches = Array.new;
# Start from #1 up to the end and assign gifts
rank.each { |cat|
cat.each { |pers|
if (!(served.exists(pers[0])) && (limit{pers[1][0]} > 0)) {
limit{pers[1][0]}--;
served{pers[0]} = 1;
matches.append(pers);
}
}
}
# Output some info to the STDOUT
matches.sort {|a,b| a[0] <=> b[0]}.each { |match|
say "#{match[0]} got #{match[1][0]} (rank: #{match[1][1]})";
}
# Output a new table
{
var new_table = parse_table(input_file);
var cols = new_table[0];
var column_pos = Hash.new;
for i in (1..ngifts) {
column_pos{cols[i]} = i;
}
# Fill in the matches
for i in (1 .. (new_table.end - 1)) {
var match = matches.find {|item| item[0] == new_table[i][0]};
var pos = column_pos{match[1][0]};
new_table[i][pos] = 'match';
}
# Update the limit number of gifts
limit.keys.each { |key|
var pos = column_pos{key};
new_table[-1][pos] = limit{key};
};
output_table(new_table, output_file);
}.run;